#include <graphics/stlloader.h>
#include <QFile>
#include <graphics/vertexarray.h>
#include <graphics/vertexbuffer.h>
#include <graphics/indexbuffer.h>
#include <graphics/mesh.h>
#include <assert.h>

namespace yam3d
{
namespace graphics
{

namespace
{
    float getMaxR( float curR, const math::vec3& newPos )
    {
        float newR = newPos.length();
        return curR > newR ? curR : newR;
    }

   // QFile file;
   // file.open()
    typedef math::vec3 StlPosition;

    void deserializeStlPosition(StlPosition& o, QFile* s )
    {
        float x,y,z;
        s->read( (char*)&x, sizeof(x) );
        s->read( (char*)&y, sizeof(y) );
        s->read( (char*)&z, sizeof(z) );
        o = math::vec3(x,y,z);
    }

    struct StlFacet
    {
        StlFacet() : normal(), vertex1(), vertex2(), vertex3(), attributeByteCount(0) {}
        virtual ~StlFacet() {}

        StlPosition	normal;
        StlPosition	vertex1;
        StlPosition	vertex2;
        StlPosition	vertex3;
        uint16_t	attributeByteCount;

        void deserialize( QFile* s )
        {
            deserializeStlPosition( normal, s );
            deserializeStlPosition( vertex1, s );
            deserializeStlPosition( vertex2, s );
            deserializeStlPosition( vertex3, s );
            s->read( (char*)&attributeByteCount, sizeof(attributeByteCount) );
        }

        StlFacet(const StlFacet& o)
        {
            *this = o;
        }

        StlFacet& operator=(const StlFacet& o)
        {
            if( this != &o )
            {
                normal = o.normal;
                vertex1 = o.vertex1;
                vertex2 = o.vertex2;
                vertex3 = o.vertex3;
                attributeByteCount = o.attributeByteCount;
            }

            return *this;
        }
    };


    struct StlFile
    {
        StlFile() : numTriangles(0), triangles()
        {
            memset( header, 0, sizeof(header) );
        }

        virtual ~StlFile()
        {
        }

        char header[80];
        uint32_t numTriangles;
        core::vector< StlFacet > triangles;

        void deserialize( QFile* s )
        {
            s->read( header, sizeof(header) );
            s->read( (char*)&numTriangles, sizeof(numTriangles) );
            triangles.resize(numTriangles);

            for( int i=0; i<triangles.size(); ++i )
            {
                triangles[i].deserialize( s );
            }

            assert(s->bytesAvailable() == 0);
        }

    private:
        StlFile(const StlFile& o);
        StlFile& operator=(const StlFile& o);
    };


}

Mesh* STLLoader::load(const core::string& fileName, float* maxRadius)
{
    if( !QFile::exists(fileName) )
    {
        qDebug("File %s does not exist!", fileName.toStdString().c_str() );
        return 0;
    }

    QFile stream(fileName);
    if( !stream.open( QFile::ReadOnly ) )
    {
        qDebug("File %s could not be opened!", fileName.toStdString().c_str() );
        return 0;
    }


    StlFile data;

    data.deserialize( &stream );
    qDebug( "%s # of triangles: %d\n", fileName.toStdString().c_str(), int(data.numTriangles) );

    float maxR(0);
    core::vector<unsigned int> triangleIndices;
    core::vector<math::vec3> positionData;
    core::vector<math::vec3> normalData;
    for( int i=0; i<data.triangles.size(); ++i )
    {
        triangleIndices.push_back(i*3+0);
        triangleIndices.push_back(i*3+1);
        triangleIndices.push_back(i*3+2);

        positionData.push_back( data.triangles[i].vertex1 );
        positionData.push_back( data.triangles[i].vertex2 );
        positionData.push_back( data.triangles[i].vertex3 );
        maxR = getMaxR( maxR, data.triangles[i].vertex1 );
        maxR = getMaxR( maxR, data.triangles[i].vertex2 );
        maxR = getMaxR( maxR, data.triangles[i].vertex3 );

        // Length of the normal must be 1, so force if it isn't
        data.triangles[i].normal.normalize();
        normalData.push_back( data.triangles[i].normal );
        normalData.push_back( data.triangles[i].normal );
        normalData.push_back( data.triangles[i].normal );

    }

    VertexArray* positions = new VertexArray(positionData);
    VertexArray* normals = new VertexArray(normalData);
    VertexBuffer* vb = new VertexBuffer();
    vb->addVertexArray("a_position",positions);
    vb->addVertexArray("a_normal",normals);

    IndexBuffer* ib = new IndexBuffer(triangleIndices);

    if( maxRadius != 0 )
    {
       *maxRadius = maxR;
    }
    return new Mesh(vb,ib);
}


}
}



